﻿// 读取Excel二进制文件
#if ENABLE_ET_COM
using System;

namespace compound_doc
{
    using System.Collections.Generic;
    using System.Runtime.InteropServices;
    using System.Runtime.InteropServices.ComTypes;
    using com_type;

    public class CUtils
    {
        public static byte[] DecodeBuffer(byte[] pBuf, ushort cce)
        {
            if(cce <= 0)
            {
                return pBuf;
            }
            // cce个长度不用
            int nCount = pBuf.Length - cce;
            byte[] ret = new byte[nCount];
            Array.Copy(pBuf, cce, ret, 0, nCount);
            return ret;
        }
    }

    class CReadCompFile
    {
        enum biff8_rec
        {
            biff8_rec_BOF           = 0x0809,
            biff8_rec_FORMULA       = 0x06,
            biff8_rec_NAME          = 0x18,
            biff8_rec_ARRAY         = 0x221,
            biff8_rec_SHRFMLA       = 0x04bc,
            biff8_rec_EXTERNNAME    = 0x23,
            biff8_rec_EXTERNSHEET   = 0x17,
            biff8_rec_BOUNDSHEET    = 0x85,
            biff8_rec_EOF           = 0x0a
        }

        // member data
        private suite_compare_cell.CDataMgr m_dataMgr;
        private string m_sFileName;

        public void ReadFile(string sFileName, suite_compare_cell.CDataMgr datMgr)
        {
            m_sFileName = sFileName;
            m_dataMgr = datMgr;
            Storage pStorage = null;
            try
            {
                if(!StorageFile.IsStorageFile(sFileName))
                {
                    throw new tool.ETImpException(tool.LogTool.LOG_TYPE.FILE_ERROR, sFileName, "非复合文档");
                }

                pStorage = StorageFile.OpenStorageFile(sFileName,
                    STGM.READ | STGM.SHARE_DENY_WRITE);
                if(pStorage == null)
                {
                    throw new tool.ETImpException(tool.LogTool.LOG_TYPE.FILE_ERROR, sFileName, "非复合文档");
                }

                CStorageNode root = new CStorageNode("RootStorage");
                CreateNodes(pStorage, root);
                CreateStorage(root);
            }
            catch(tool.ETImpException ex)
            {
                if(ex.FileName == null)
                {
                    ex.FileName = sFileName;
                }
                throw ex;
            }
            catch (System.Exception ex)
            {
                throw new tool.ETImpException(tool.LogTool.LOG_TYPE.BINARY_FAILED, sFileName, "读取二进制文件未知错误:", ex);
            }
            finally
            {
                pStorage.Dispose();
                pStorage = null;
            }
        }

        protected bool CreateNodes(Storage pStorage, CStorageNode root)
        {
            CEnumStorage enumStorage = new CEnumStorage();
            enumStorage.Init(pStorage);
            int stgty = 0;
            CNode pNode = null;
            while(0 == enumStorage.Next(ref stgty, ref pNode))
            {
                root.AddNode(pNode);
                switch(stgty)
                {
                    case (int)STGTY.STORAGE:
                        {
                            CStorageNode pStorageNode = (CStorageNode)pNode;
                            Storage pStream = pStorage.OpenStorage(pStorageNode.GetName(),
                                    STGM.SHARE_EXCLUSIVE | STGM.READ);
                            bool bRet = true;
                            if (null != pStream)
                            {
                                bRet = CreateNodes(pStream, pStorageNode);
                            }
                            pStream.Dispose();
                            pStream = null;
                            return bRet;
                        }
                    case (int)STGTY.STREAM:
                        {
                            CStreamNode pStreamNode = (CStreamNode)pNode;
                            Stream pStream = pStorage.OpenStream(pStreamNode.GetName(),
                                STGM.SHARE_EXCLUSIVE | STGM.READ);
                            if (pStream == null)
                            {
                                return false;
                            }
                            byte[] bCache = new byte[pStreamNode.GetSize()];
                            pStream.Read(bCache, pStreamNode.GetSize());
                            pStreamNode.SetData(bCache);
                            pStream.Dispose();
                            pStream = null;
                        }
                        break;
                }
            }
            return true;
        }

        public bool CreateStorage(CStorageNode root)
        {
            int nSize = root.GetNodeListSize();
            int index = -1;
            for(int i = 0; i < nSize; ++i)
            {
                CNode p = root.GetNodeByIndex(i);
                // 我只关心Workbook的子节点
                // 如果有ETSharedDocument那么就用此节点
                if (p.GetName() == "Workbook" && index == -1)
                {
                    index = i;
                }
                else if (p.GetName() == "ETSharedDocument")
                {
                    index = i;
                }
            }
            if(index < 0)
            {
                throw new tool.ETImpException(tool.LogTool.LOG_TYPE.FILE_ERROR, m_sFileName, "二进制错误");
            }

            CNode node = root.GetNodeByIndex(index);
            switch (node.GetNodeType())
            {
                case NODE_TYPE.STORAGE:
                    {
                        CStorageNode pStorageNode = (CStorageNode)node;
                        return CreateStorage(pStorageNode);
                    }
                case NODE_TYPE.STREAM:
                    {
                        CStreamNode pStreamNode = (CStreamNode)node;
                        return CreateStream(pStreamNode);
                    }
                default:
                    break;
            }

            return true;
        }

        protected bool CreateStream(CStreamNode pStreamNode)
        {
            byte[] pData = null;
            ushort uSize = 0;
            ushort uMark = 0;

            STREAM_STATE SS = STREAM_STATE.NONE;
            CEnumRec enumRec = new CEnumRec();
            enumRec.Init(pStreamNode.GetData());
            while(enumRec.NextRec(ref pData, ref uSize, ref uMark))
            {
                // 我们只关心Formula、ArrayFmla、ShrFmla、Name
                bool bOK = true;
                switch(uMark)
                {
                    case (ushort)biff8_rec.biff8_rec_BOF:
                        bOK = HandleBOF(ref SS);
                        break;
                    case (ushort)biff8_rec.biff8_rec_EOF:
                        bOK = HandleEOF(ref SS);
                        break;
                    case (ushort)biff8_rec.biff8_rec_NAME:
                        bOK = (SS == STREAM_STATE.BOOK_BOF);
                        m_dataMgr.AddNameRec(pData);
                        break;
                    case (ushort)biff8_rec.biff8_rec_EXTERNSHEET:
                        bOK = (SS == STREAM_STATE.BOOK_BOF);
                        m_dataMgr.AddExternSheetRec(pData);
                        break;
                    case (ushort)biff8_rec.biff8_rec_EXTERNNAME:
                        bOK = (SS == STREAM_STATE.BOOK_BOF);
                        m_dataMgr.AddExternNameRec(pData);
                        break;
                    case (ushort)biff8_rec.biff8_rec_BOUNDSHEET:
                        bOK = (SS == STREAM_STATE.BOOK_BOF);
                        m_dataMgr.AddBoundSheetRec(pData);
                        break;
                    case (ushort)biff8_rec.biff8_rec_FORMULA:
                        bOK = (SS == STREAM_STATE.SHEET_BOF);
                        m_dataMgr.AddFormulaRec(pData);
                        break;
                    case (ushort)biff8_rec.biff8_rec_ARRAY:
                        bOK = (SS == STREAM_STATE.SHEET_BOF);
                        m_dataMgr.AddArrFmlaRec(pData);
                        break;
                    case (ushort)biff8_rec.biff8_rec_SHRFMLA:
                        bOK = (SS == STREAM_STATE.SHEET_BOF);
                        m_dataMgr.AddShrFmlaRec(pData);
                        break;
                }

                if (!bOK)
                {
                    throw new tool.ETImpException( tool.LogTool.LOG_TYPE.FILE_ERROR, m_sFileName, 
                        string.Format("failed of analyze binary: Cur STREAM_STATE=", SS.ToString()) );
                }
            }

            return true;
        }

        protected bool HandleBOF(ref STREAM_STATE SS)
        {
            switch (SS)
            {
                case STREAM_STATE.BOOK_BOF:
                case STREAM_STATE.SHEET_BOF:
                    return false;
                case STREAM_STATE.NONE:
                    SS = STREAM_STATE.BOOK_BOF;
                    break;
                case STREAM_STATE.BOOK_EOF:
                case STREAM_STATE.SHEET_EOF:
                    m_dataMgr.BeginSheet();
                    SS = STREAM_STATE.SHEET_BOF;
                    break;
            }
            return true;
        }

        protected bool HandleEOF(ref STREAM_STATE SS)
        {
            switch (SS)
            {
                case STREAM_STATE.BOOK_BOF:
                    SS = STREAM_STATE.BOOK_EOF;
                    break;
                case STREAM_STATE.SHEET_BOF:
                    m_dataMgr.EndSheet();
                    SS = STREAM_STATE.SHEET_EOF;
                    break;
                case STREAM_STATE.NONE:
                case STREAM_STATE.BOOK_EOF:
                case STREAM_STATE.SHEET_EOF:
                    return false;
            }
            return true;
        }

        internal enum STREAM_STATE
        {
            NONE,
            BOOK_BOF,
            BOOK_EOF,
            SHEET_BOF,
            SHEET_EOF
        };
    }
}
#endif // #if ENABLE_ET_COM